package com.feri.common.util;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.beetl.core.Configuration;
import org.beetl.core.GroupTemplate;
import org.beetl.core.Template;
import org.beetl.core.resource.ClasspathResourceLoader;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * ━━━━━━神兽出没━━━━━━
 * 　　　┏┓　　　┏┓
 * 　　┏┛┻━━━┛┻┓
 * 　　┃　　　　　　 ┃
 * 　　┃　　　━　　　┃
 * 　　┃　┳┛　┗┳　  ┃
 * 　　┃　　　　　　 ┃
 * 　　┃　　　┻　　　┃
 * 　　┃　　　　　　　┃
 * 　　┗━┓　　　┏━┛Code is far away from bug with the animal protecting
 * 　　　　┃　　　┃    神兽保佑,代码无bug
 * 　　　　┃　　　┃
 * 　　　　┃　　　┗━━━┓
 * 　　　　┃　　　　　　　┣┓
 * 　　　　┃　　　　　　　┏┛
 * 　　　　┗┓┓┏━┳┓┏┛
 * 　　　　　┃┫┫　┃┫┫
 * 　　　　　┗┻┛　┗┻┛
 * ━━━━━━永无BUG!━━━━━━
 *
 * @Description:
 * @Author: 王磊
 * @Date: 2022/9/8 16:11
 */
public class CodeCreate {
    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    static class DbField {
        private String name;//名称
        private String type;//类型
        private String note;//注释
        private String colname;
    }

    //对应一次性创建的包对应的文件夹
    private static final String[] pks = new String[]{"entity", "dao", "service/intf", "service/impl", "controller", "vo", "util", "config"};

    /**
     * 获取数据库的表信息
     *
     * @param ip       数据库地址
     * @param port     数据库端口号
     * @param password 数据库密码
     * @param dbname   数据库名
     */
    public static Map<String, List<DbField>> getTables(String ip, int port, String dbname, String password) throws Exception {
        //1.加载驱动
        Class.forName("com.mysql.cj.jdbc.Driver").newInstance();
        //2.数据库连接
        Connection conn = DriverManager
                .getConnection("jdbc:mysql://" + ip + "." + port + "/" + dbname, "root", password);
        //3.获取元数据
        DatabaseMetaData metaData = conn.getMetaData();
        //4.获取数据库中的所有表的信息
        ResultSet tables = metaData.getTables(dbname, "%", "%", new String[]{"TABLE"});
        Map<String, List<DbField>> classes = new LinkedHashMap<>();
        //5.遍历 获取每一个表名
        while (tables.next()) {

            //6.获取表名
            String table = tables.getString("TABLE_NAME");

            List<DbField> fields = new ArrayList<>();
            //7.获取表中的字段信息
            ResultSet columns = metaData.getColumns(dbname, "%", table, "%");
            //8.遍历表中所有字段信息
            while (columns.next()) {
                String colname = columns.getString("COLUMN_NAME");
                String type = columns.getString("TYPE_NAME");
                String note = columns.getString("REMARKS");

                fields.add(new DbField(parselowname(colname),parseType(type), note,colname));
            }
            classes.put(table, fields);
        }
        tables.close();
        conn.close();
        return classes;
    }

    /**
     * 根据模板，生成源文件
     *
     * @param groupTemplate 模板文件所在的文件夹
     * @param btl           模板文件名
     * @param params        模板文件的变量参数
     * @param path          源文件路径
     */
    public static void createJava(GroupTemplate groupTemplate, String btl, Map<String, Object> params, String path) throws IOException {
        //1.获取模板
        Template template = groupTemplate.getTemplate(btl);
        //2.为模板设置变量的内容
        template.binding(params);
        //3.生成文件
        template.renderTo(new FileWriter(path));
    }

    /**
     * 实现数据类型转换 Mysql的数据类型 转换 Java的数据类型
     *
     * @param type 数据库的数据类型
     */
    public static String parseType(String type) {
        String r;
        switch (type) {
            case "INT":
            case "TINYINT":
                r = "Integer";
                break;
            case "BIGINT":
                r = "Long";
                break;
            case "DOUBLE":
            case "DECIMAL":
                r = "Double";
                break;
            case "DATE":
            case "DATETIME":
                r = "Date";
                break;
            default:
                r = "String";
                break;
        }
        return r;
    }

    /**
     * 实现表名转换为类名 大驼峰式命名
     *
     * @param name 表名
     * @return 大驼峰式命名的类名
     */
    public static String parseClsName(String name) {
        //1.验证表名是否包含_
        if (name.contains("_")) {
            //2.切割，获取每个单词
            String[] arr = name.split("_");

            StringBuffer buffer = new StringBuffer();

            //3.循环遍历单词
            for (String s : arr) {
                //4.如果单词长度大于1才拼接为类名
                if (s.length() > 1) {
                    //首字母大写，其余字母
                    buffer.append(s.substring(0, 1).toUpperCase() + s.substring(1).toLowerCase());
                }
            }
            return buffer.toString();
        } else {
            //变为大驼峰式命名
            return name.substring(0, 1).toUpperCase() + name.substring(1).toLowerCase();
        }
    }
    /**
     * 实现字段名转换为属性名 小驼峰式命名
     *
     * @param name 字段名
     * @return 小驼峰式命名的类名
     */
    public static String parselowname(String name){
        //1.验证字段名是否包含_
        if (name.contains("_")) {
            //2.切割，获取每个单词
            String[] arr = name.split("_");

            StringBuffer buffer = new StringBuffer();

            //3.循环遍历单词
            for (String s : arr) {
                //4.如果单词长度大于1才拼接为类名
                if (s.length() > 1) {
                    //首字母大写，其余字母
                    buffer.append(s.substring(0, 1).toUpperCase() + s.substring(1).toLowerCase());
                }
            }
            String str= buffer.toString();
            char[] chars = str.toCharArray();
            if (chars[0]>='A'&&chars[0]<='Z') {
                chars[0]+=32;
            }
            return String.valueOf(chars);
        } else {
            //变为小驼峰式命名
            return name;
        }

    }
    /**
     * 实现包名对应的文件夹的创建
     */
    public static void mkdir(String path) {
        for (String s : pks) {
            File dir = new File(path, s);
            if (!dir.exists()) {
                dir.mkdirs();
            }
        }
    }

    /**
     * 获取数据库的表信息
     *
     * @param ip       数据库地址
     * @param port     数据库端口号
     * @param password 数据库密码
     * @param dbname   数据库名
     * @param author   作者
     * @param pkname   项目的包名
     * @return 生成源码是否成功
     */
    public static boolean createCode(String ip, int port, String dbname, String password, String author, String pkname) {
        try {
            //1.加载模板文件组对象
            ClasspathResourceLoader loader = new ClasspathResourceLoader("templates");
            Configuration cfg = Configuration.defaultConfiguration();
            GroupTemplate groupTemplate = new GroupTemplate(loader, cfg);

            //2.获取数据库表信息
            Map<String, List<DbField>> tables = getTables(ip, port, dbname, password);

            //3.创建包对应的文件夹
            String path = "src/main/java/" + pkname.replaceAll("[.]", "/");
            mkdir(path);
            Map<String, Object> params = new HashMap<>();
            params.put("pkname", pkname);
            params.put("author", author);
            params.put("ctime", new SimpleDateFormat("yyyy/MM/dd HH:mm:ss").format(new Date()));

            //4.循环遍历 所有的表
            for (String s : tables.keySet()) {
                List<DbField> fields = tables.get(s);
                //设置通用的变量集合
                String clsname = parseClsName(s);
                params.put("clsname", clsname);

                //4.生成实体类
                Map<String, Object> params1 = new HashMap<>(params);
                params1.put("tname", s);
                params1.put("fields", fields);
                createJava(groupTemplate, "entity.btl", params1, path + "/entity/" + clsname + ".java");

                //5.生成持久类
                createJava(groupTemplate, "dao.btl", params, path + "/dao/" + clsname + "Dao.java");

                //6.生成业务接口类
                createJava(groupTemplate, "service.btl", params, path + "/service/intf/" + clsname + "Service.java");

                //7.生成业务实现类
                createJava(groupTemplate, "serviceimpl.btl", params, path + "/service/impl/" + clsname + "ServiceImpl.java");

                //8.生成控制层类
                createJava(groupTemplate, "controller.btl", params, path + "/controller/" + clsname + "Controller.java");
            }
            //9.生成R类
            createJava(groupTemplate, "r.btl", params, path + "/vo/R.java");

            return true;
        } catch (Exception e) {
            return false;
        }
    }
}
